// 死亡倒计时复活插件

string author = "Live";
string pluginName = "DelayRespawn 死亡复活";
string version = "3.1";

// 服务器复活指令
// CASConVar@ sv_respawnTime = null;
// CASConVar@ sv_respawnHealth = null;

// 复活的时间设置
int respawnTime = 30;
// 玩家的初始血量
int respawnHealth = 100;

// 当插件被初始化
void OnPluginInit()
{
	// 设置插件信息
	PluginData::SetAuthor(author);
	PluginData::SetName(pluginName);
	PluginData::SetVersion(version);

	// 提示
	ThePresident.InfoFeed("插件:"+pluginName+"| 版本:"+version+"| 作者:"+author,false);
        
        // 当没有存活的玩家时
        Events::ThePresident::OnNoSurvivorsRemaining.Hook( @OnNoSurvivorsRemaining );
        // 当玩家断开连接时
        Events::Player::OnPlayerDisconnected.Hook( @OnPlayerDisconnected );
        // 当玩家出生时
        Events::Player::OnPlayerSpawn.Hook( @OnPlayerSpawn );
        // 当玩家被杀死后
        Events::Player::OnPlayerKilledPost.Hook( @OnPlayerKilledPost );

        // 初始化复活时间的指令
        // @sv_respawnTime = ConVar::Create( "live_respawnTime", formatInt(respawnTime), "玩家死亡后自动复活需要等待的时间。The time required to automatically respawn after a player's death.", true, 0, true, 180 );
        // @sv_respawnHealth = ConVar::Create( "live_respawHealth",formatInt(respawnHealth), "玩家复活后的血量。The player's health after resurrection.", true, 1, true, 999 );

}

void OnPluginUnload()
{
	// ConVar::Remove( sv_respawnTime );
	// ConVar::Remove( sv_respawnHealth );
}

// 游戏结束标识位（如果游戏结束，终止复活）
bool gameover = false;

// 玩家复活倒计时数组
// 因为官方提供的Schedule::Task计时方法只能接受一个参数并且必须是int，再加上其运行特殊性，无法同时传递计时和玩家槽位id
// 我的想法是，以玩家槽位ID作为索引对应该数组元素为复活时间，即如果entindex()为x，则它使用respawnSecondp[x-1]（因为entindex从1开始。数组索引从0开始）作为该玩家的复活时间
// （一个玩家一个位置，可按照最大人数自行更改，此处为当前版本最大人数16个）

array<int> respawnTimeList(16,respawnTime);
// 这个数组是用来解决BUG问题的，复活标识，0 不能复活 1 能复活，防止因为各种正在复活时的突发重生行为（玩家被命令重生，防守模式中回合胜利的自动重生）
// 从而导致复活后倒计时复活还在运作导致再复活一次的BUG
array<bool> canRespawnList(20,false);
 

// 当幸存者玩家全部死亡
HookReturnCode OnNoSurvivorsRemaining( int iCandidate )
{
    // 幸存者玩家全部死亡,中断所有复活方法
    gameover = true;
    //重置复活计时数组，防止因游戏结束后部分玩家复活停止导致的数据变化
    respawnTimeList = array<int>(16,respawnTime);
	return HOOK_CONTINUE;
}

// 当玩家断开连接后
HookReturnCode OnPlayerDisconnected(CTerrorPlayer@ pPlayer)
{
    if ( pPlayer is null ) 
        return HOOK_HANDLED;
    int index = pPlayer.entindex();
    // 将复活标识设为false,防止BUG
    canRespawnList[index-1] = false;
    // 重置掉该玩家槽位的复活时间
    respawnTimeList[index-1] = respawnTime;
	return HOOK_CONTINUE;
}


// 当玩家重生时(重生不是指变成人，而是指玩家被分配为一个游戏角色)
HookReturnCode OnPlayerSpawn(CTerrorPlayer@ pPlayer)
{
    if ( pPlayer is null ) 
        return HOOK_HANDLED;
    // 获取当前重生体的身份
    // 如果是人复活重生了
    if ( pPlayer.GetTeamNumber() == TEAM_SURVIVOR ){
        // 将复活标识设为false，防止因其它因素复活（命令复活、守卫模式回合复活）后还在倒计时复活的BUG
        int index = pPlayer.entindex();
        canRespawnList[index-1] = false;
        // 重置复活时间
        respawnTimeList[index-1] = respawnTime;
       
        // 重置血量(防止连续图复活血量不完整)
        pPlayer.SetMaxHealth(respawnHealth);
        pPlayer.SetHealth(pPlayer.GetMaxHealth());
    }else{
        // 获取当前的游戏模式
        int gamemode = ThePresident.GetGameModeType();
        // 这个游戏中途加入的人会变成小强或者丧尸，为了让其复活
        // 如果游戏不是猎杀模式
        if( not (gamemode == CT_HUNTED) ){
            // 延时5秒调用检查方法，防止死亡的玩家一瞬间变成旁观者（守卫模式），复活时间还是second秒没来得及减少导致判断错误的BUG
            Schedule::Task(5.0f,pPlayer.entindex(),check);
        }
    }
	return HOOK_CONTINUE;
}

// 用于检查玩家是否是不能复活的玩家
void check(int index){
    CTerrorPlayer@ pPlayer = ToTerrorPlayer( index );
    // 获取当前玩家的身份
    int TeamNumber = pPlayer.GetTeamNumber();
    //Chat.PrintToChat(all,""+TeamNumber);
    // 如果当前玩家身份是旁观者或者丧尸（再判断一次，防止是0）
    if(TeamNumber == TEAM_SPECTATOR || TeamNumber == TEAM_ZOMBIE){
		// 判断该角色的复活时间是不是second（正在复活会减少时间，调用前已延迟5秒防止速度太快误判），即可分辨是死亡后正在复活的小强或者丧尸，还是中途进入没在复活的小强
		if(respawnTimeList[index-1] == respawnTime){
			if(!gameover){
				// 复活玩家
				respawnPlayer(index);
				Chat.PrintToChat(all, "{gold}[{green}Live{gold}]{white}:检测到玩家["+pPlayer.GetPlayerName()+"]加入时为小强或丧尸，已自动得到重新做人的机会" );
			}
		}
    }
}


//当玩家死亡后
HookReturnCode OnPlayerKilledPost(CTerrorPlayer@ pPlayer)
{
    if ( pPlayer is null ) 
        return HOOK_HANDLED;
    // 输出提示信息
    Chat.CenterMessage(all,"["+pPlayer.GetPlayerName()+"]的屁股被丧尸给撅了...(["+pPlayer.GetPlayerName()+"]has died)");
    // 输出测试信息
    Chat.PrintToConsole(all,pPlayer.GetPlayerName()+" 死亡 - "+pPlayer.entindex());
	// 获取玩家身份
	int TeamNumber = pPlayer.GetTeamNumber();
	// 是人才给复活，防止BUG
	if(TeamNumber == TEAM_SURVIVOR){
		int index = pPlayer.entindex();
		// 设置复活标识位为真
		canRespawnList[index-1] = true; 
		// 开始倒计时方法
		Schedule::Task( 0.0f, pPlayer.entindex(), tip );
    }
    return HOOK_CONTINUE;
}


//提示死亡计时
void tip(int index){ 
    // 游戏是否结束
    if( not gameover ){
        // 该玩家的复活标识位，以便其它事件随时终止复活
        if( canRespawnList[index-1] ){
            // 倒计时提示玩家
            CBasePlayer@ pPlayer = ToBasePlayer( index );
            Chat.CenterMessagePlayer(pPlayer,respawnTimeList[index-1]+"秒后复活...(Waiting for resurrection "+respawnTimeList[index-1]+")");
            // 递减该玩家对应数组中的时间
            respawnTimeList[index-1]--;
            // Chat.PrintToChat(all,"时间减少了");
            // 倒计时还大于-1秒就继续调用
            if(respawnTimeList[index-1] > -1 )
                // 这里做了一个递归函数（因为我发现官方的Task方法很奇怪，当使用循环时，会将时间和结果堆叠在一起并在一个方法结束后输出）
                // 每一秒调用一次计时方法
                Schedule::Task(1.0f,index,tip);
            else{
				// Chat.PrintToChat(all,"倒计时归零，复活");
                // 倒计时归零，结束递归，转调用复活玩家的方法
                respawnPlayer(index);
            }
        }
    }
}

//复活玩家
void respawnPlayer(int index){
    //重置被复活玩家的复活时间值
    respawnTimeList[index-1] = respawnTime;
    CTerrorPlayer@ pPlayer = ToTerrorPlayer( index );
    if ( pPlayer is null ) return;
    // 修改被复活玩家的游戏阵营
    pPlayer.ChangeTeam( TEAM_SURVIVOR );
    // 复活玩家
    pPlayer.Respawn();
    // 提示玩家复活
    Chat.CenterMessage(all, "["+pPlayer.GetPlayerName()+"]从石头里蹦出来了！(["+pPlayer.GetPlayerName()+"]has been resurrected)");
    // 输出测试信息
    Chat.PrintToConsole(all,"[Live]插件测试信息:"+pPlayer.GetPlayerName()+" 复活");
    // 随机传送到一个队友，防止有的傻逼图只会复活在出生点
    CTerrorPlayer@ AlivePlayer = GetRandomPlayer(false, true);
    pPlayer.SetAbsOrigin(AlivePlayer.GetAbsOrigin());
}

// 地图开始
void ThePresident_OnMapStart()
{
	ThePresident_OnRoundStart();
}

// 回合开始
void ThePresident_OnRoundStart()
{
	// 游戏开始
	gameover = false;
}
